#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <memory.h>
#include "list.h"

// total number of bytes allocated
static unsigned long total_allocated = 0;
// total number of bytes freed
static unsigned long total_freed = 0;

// Pointer to the original free() call
static void (*_free)(void *) = NULL;
// Pointer to the original malloc() call
static void *(*_malloc)(size_t size) = NULL;
// the address returned by the original malloc() call
static void *malloc_res = NULL;

// the data structure that contains the address pointer
typedef struct addr_t
{
	void * addr;			//the start pointer of the address
	unsigned long  bytes;		//the bytes allocated
	struct hlist_node list;		//the link list used to link to other nodes
} addr_t;

// the hash list head
struct hlist_head addrlist = HLIST_HEAD_INIT;

// the free wrapper function call
void free(void *addr)
{
//	printf("shared object free get called!\n");

	if(addr == NULL)
		return;

	//initialization
	if(_free == NULL)
	{
		_free = (void(*)(void *))dlsym(RTLD_NEXT, "free");
	}
	
	addr_t * q = NULL;
	struct hlist_node * p = NULL;
	hlist_for_each(p, &addrlist)
	{
		q = hlist_entry(p, addr_t, list);
		if(q->addr == addr)
			break;
	}

	if(p)
	{
		total_freed += q->bytes;
		_free(q);
	}	
	else
	{
		
		printf("*** MY PROGRAM DETECTED ***: double free or free of invalid address: %p ***\n", addr);
	}
	_free(addr);
	_free = NULL;
}

//the malloc() wrapper function call
void* malloc(size_t size)
{
//	printf("shared object malloc get called!\n");
	//initialization
	if(_malloc == NULL)
		_malloc = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc");
	
	
	malloc_res = _malloc(size);
	if(malloc_res == NULL)
		return NULL;

	addr_t * aa = (addr_t *)_malloc(sizeof(addr_t));
	INIT_HLIST_NODE(&aa->list);
	
	aa->addr = malloc_res;
	aa->bytes = size;
	hlist_add_head(&aa->list, &addrlist);
	
	total_allocated += size;
	printf("memory allocation address %p\n", malloc_res);
	return malloc_res;
}


// the constructor: invoked at the beginning of the program
__attribute__((constructor))
	static void initialize_so() {
		printf("Hi~~~\n");
	}

// the destructor: invoked at the end of the program
__attribute__((destructor))
	static void destroy_so() {
		printf("Goodbye~~~\n");
		printf("total amounts of bytes allocated:\t%lu\n", total_allocated);
		printf("total amounts of bytes freed:\t\t%lu\n", total_freed);
	}
